import os
import sys
import logging                                                                  # запись логов
import re
import sqlite3

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *



sys.path.append('c:\PyLib')
from WebKit import *

from datetime import date

# дата в формате '2014-08-14'
now = date.today().strftime('%Y-%m-%d')

logging.basicConfig(filename='lib.log',level=logging.DEBUG,
    format = u'%(filename)s[LINE:%(lineno)d]# %(levelname)-8s %(message)s')


class MainForm(QWidget):


    def __init__(self, db_name, out_name, parent=None):
        super().__init__()

        self.db_name = db_name
        self.out_name = out_name
        self.create_db()
        self.create_ui()
        self.init_ui()


    def create_db(self):

        """ СОЗДАНИЕ БАЗЫ ДАННЫХ.
        -- Создаем папку базы данных. Создаем базу данных с таблицами."""

        if not os.path.exists('db'): os.makedirs('db')
        self.conn = sqlite3.connect('db/{}.db'.format(self.db_name),
            isolation_level = None)
        query = self.conn.cursor()
        query.execute("CREATE TABLE IF NOT EXISTS vacant ("
            "url TEXT DEFAULT '',"
            "org TEXT DEFAULT '',"
            "face TEXT DEFAULT '',"
            "phone TEXT DEFAULT '',"
            "phone_find TEXT DEFAULT '',"
            "org_CRM TEXT DEFAULT '',"
            "curator TEXT DEFAULT '');")
        query.execute("CREATE TABLE IF NOT EXISTS ref "
            "(date TEXT, url TEXT DEFAULT '');")
        query.execute("CREATE TABLE IF NOT EXISTS org "
            "(name TEXT DEFAULT '');")


    def create_ui(self):

        """ СОЗДАНИЕ ФОРМЫ.
        -- Кнопки помещаем в горизотальный бокс с затычкой справа.
        -- В вертикальный бокс помещаем бокс с кнопками, URL и броузер."""

        hbox = QHBoxLayout()

        btn_run = QPushButton(" ПОЛНЫЙ ЦИКЛ ")
        # btn_run.setStyleSheet('QPushButton {font-size: 12pt; font-weight: bold; color: red;}')
        # from __main__ import full_cycle
        # self.btn_execute.clicked.connect(full_cycle)
        btn_run.clicked.connect(self.full_cycle)
        hbox.addWidget(btn_run)

        hbox.addStretch()

        self.functions = QComboBox()
        self.functions.currentIndexChanged.connect(self.functions_changed)
        hbox.addWidget(self.functions)

        self.btn_execute = QPushButton("ВЫПОЛНИТЬ")
        self.btn_execute.clicked.connect(self.execute)
        hbox.addWidget(self.btn_execute)

        vbox = QVBoxLayout()
        vbox.addLayout(hbox)


        self.line = QLineEdit()
        self.web = Browser()

        vbox.addWidget(self.line)
        vbox.addWidget(self.web)

        self.pb = QProgressBar()
        vbox.addWidget(self.pb)

        self.setLayout(vbox)
        self.setWindowTitle(self.db_name)
        self.setWindowIcon(QIcon('pict\\main.ico'))
        self.resize(700, 500)
        self.show()


    def init_ui(self):

        """ ЗАПОЛНЕНИЕ ПОЛЕЙ ФОРМЫ.
        -- Создаём словарь функций программы. Создаём модель для поля <функции>.
        """

        self.D = {
            ' 1.Загрузить куки' : 'cookies_load(self)',
            ' 2.Логин ' : 'login(self)',
            ' 3.Собрать ссылки ' : 'collect_ref(self)',
            ' 4.Удалить старые ссылки ' : 'del_old_ref(self)',
            ' 5.Добавить ссылки в архив ' : 'add_ref(self)',
            ' 6.Загрузить вакансии ' : 'load_vacant(self)',
            ' 7.Удаляем дубли и баним ' : 'clean_org(self)',
            ' 8.Проверка по CRM ' : 'find_CRM(self)',
            ' 9.Вывод ' : 'out(self)',
            '10.Сохранить куки' : 'cookies_save(self)'
            }
        l = list(self.D.keys())
        l.sort()
        self.model_functions = QStringListModel(l, self)
        self.functions.setModel(self.model_functions)


    def execute(self):

        """ ПОШАГАВОЕ ВЫПОЛЕНИЕ ОПЕРАЦИЙ.
        -- Выполняем все команды из массива full_cycle."""

        from __main__ import collect_ref, load_vacant, login
        exec(self.D[self.functions.currentText()])
        self.btn_execute.setEnabled(False)


    def full_cycle(self):

        """ ПОЛНЫЙ ЦИКЛ РАБОТЫ ПАУКА.
        -- Выполняем все команды из массива full_cycle."""

        from __main__ import collect_ref, load_vacant, all_cycle
        for step in all_cycle:
            print(step)
            exec(step)


    def functions_changed(self,index):

        self.btn_execute.setEnabled(True)


def cookies_load(form):

    """ ЗАГРУЖАЕМ КУКИ.
    -- Куки лежат в папке cookies. Если файл с куками есть, то загружаем."""

    if not os.path.exists('cookies'): os.makedirs('cookies')
    file = 'cookies/'+ form.db_name + '.txt'
    if os.path.exists(file):
        f = open(file, 'r')
        allC = tuple(cooky for li in f
            for cooky in QNetworkCookie.parseCookies(li.rstrip()))
        if allC:
            form.web.Сookies.setAllCookies(allC)
        f.close()


def cookies_save(form):

    """ СОХРАНЯЕМ КУКИ.
    -- Куки лежат в папке cookies."""

    if not os.path.exists('cookies'): os.makedirs('cookies')
    file = 'cookies/'+ form.db_name + '.txt'
    f = open(file, 'wb')
    allC = form.web.Сookies.allCookies()
    for c in allC:
        f.write(c.toRawForm()+'\r\n')
    f.close()



def del_old_ref(form):

    """ УДАЛЯЕМ ВЧЕРАШНИЕ ССЫЛКИ.
    -- В запросе условие (AND '{}' > r.date;) необходимо, чтобы при повторном запуске
    выдавались сегодняшние вакансии, а не вакансии между запусками. Это защита от
    случайного повторного запуска программы.
    """

    query = form.conn.cursor()                                                       # курсор SQLite3



    sql = ("DELETE FROM vacant WHERE ROWID IN "                                 # удалить из ссылки сохраненные в ref
        "(SELECT v.ROWID FROM vacant as v, ref as r "                           # если дата архивной ссылки старая
        "WHERE (v.url= r.url) AND ('{}' > r.date));".format(now))

    try:
        query.execute(sql)                                                      # удаляем ссылки
    except sqlite3.Error as e:
        logging.critical('sqlite: {} {}'.format( e.args[0],sql))
        QMessageBox.critical(None, "КРИТИЧЕСКАЯ ОШИБКА", "Подробности в log.txt.")
        quit()

    query.close()


def add_ref(form):

    """
    Ссылки на вакансии из рабочей таблицы добавляются в архив ссылок.
    Записываем URL и текущую дату для контроля.
    """
    query = form.conn.cursor()                                                       # курсор SQLite3


    sql = ("INSERT INTO ref (date, url) SELECT '{}', url FROM vacant;".         # сливаем ссылки в таблицу ref
        format(now))
    try:
        query.execute(sql)
    except sqlite3.Error as e:
        logging.critical('sqlite: {} {}'.format( e.args[0],sql))
        QMessageBox.critical(None, "КРИТИЧЕСКАЯ ОШИБКА", "Подробности в log.txt.")
        quit()

    query.close()


def clean_org(form):

    """ УДАЛЕНИЕ ВАКАНСИЙ.
    -- Без названия организации или без телефона.
    -- С одинаковыми названиями организаций.
    -- С одинаковыми телефонами.
    -- Забаненых организаций."""

    query = form.conn.cursor()

    query.execute("DELETE FROM vacant WHERE (phone = '') OR (org = '');")

    query.execute("DELETE FROM vacant WHERE ROWID NOT IN ("
        "SELECT MIN(ROWID) FROM vacant GROUP BY org);")

    query.execute("DELETE FROM vacant WHERE ROWID NOT IN ("
        "SELECT MIN(ROWID) FROM vacant GROUP BY phone);")

    query.execute("DELETE FROM vacant WHERE ROWID IN "
        "(SELECT v.ROWID FROM vacant as v, org as r WHERE v.org=r.name);")

    query.close()


def find_CRM(form):
    """ ПРОВОВЕРКА ПО CRM (соединение с SQLite)
    -- Соединение базами данных.
    -- Читаем последовательно таблицу. Нормализуем телефоны.
    -- Формируем по нормализованным телефонам запрос и ищем в ЛЕКСКМЕ.
    --
    --
    """

    def phone_norm(phones):
        """
        Нормализация телефонов.
        Сотовые до 10 цифр.
        Самарские стационарные до 7 цифр.
        Тольяттинские стационарные до 6 цифр.
        """
        phones = ''.join(filter(lambda x: x.isalnum() or x == ',', phones))         # оставляем буквы, цыфры, запятые
        phones = phones.strip(',').split(',')                                       # убрать последнюю запятую и создать список

        for i, phone in enumerate(phones):
            p = phone.find('до')                                                    # убрать добавочный номер
            if p != -1:                                                             # всё после слов дополниельный или добавочный
                phone = phone[:p]
            if len(phone)>=11:                                                      # убрать код России - урезать до 10 цифр
                phone = phone[1:]
            if phone[:3]== '846':                                                   # убрать код Самары
                phone = phone[3:]
            if phone[:4]== '8482':                                                  # убрать код Тольятти
                phone = phone[3:]
            phones[i] = phone
        phones.sort()
        return(','.join(phones))# возвращаем сортированный список телефонов

    dbMS = QSqlDatabase.addDatabase("QODBC")                                    # соедиение с лексемой
    dbMS.setDatabaseName("lexema")
    dbMS.setUserName("sa")
    dbMS.setPassword("1953")
    if dbMS.open():
        print("соединение lexema")

    lexema = QSqlQuery()
    update = form.conn.cursor()                                                      # для обновления записей
    select = form.conn.cursor()                                                      # для обхода записей
    select.execute("SELECT ROWID, phone, phone_find, org_crm, curator FROM vacant;")                     # читать телефоны

    for rowid, phone, phone_find, org_crm, curator in select:
        phone_find = phone_norm(phone)                                     # нормализация телефонов

        phones = phone_find.split(',')  # список телефонов
        where = ["Phone LIKE '%{}%'".format(phone) for phone in phones]
        where = ' OR '.join(where)
        sql = ("SELECT s.fullname, u.name FROM dbo.Spr_Org s "              # запрос на поиск в телефонам
               "INNER JOIN dbo.Unianalit u ON u.vcode = s.kurator WHERE {}"
               .format(where))

        # logging.debug('find_CRM: {}'.format(sql))

        lexema.exec(sql)

        if lexema.next():                                                   # первая попавшаяся запись
            curator = re.match('[^ ]*',lexema.value(1)).group()             # отбросить имя и отчество куратора
            org_crm = "({}){}".format(curator, lexema.value(0).rstrip())    # название организации в лексеме

        sql = ("UPDATE vacant SET phone_find = ?, org_crm = ?, curator = ? "
               "WHERE ROWID = {}".format(rowid))
        try:
            update.execute(sql,(phone_find, org_crm, curator))
        except sqlite3.Error as e:
            logging.critical('sqlite: {} {}'.format( e.args[0],sql))
            QMessageBox.critical(None, "КРИТИЧЕСКАЯ ОШИБКА", "Подробности в log.txt.")
            quit()

    update.close()
    select.close()


def out(form):

    """ ВЫВОД (указатель на форму)
    -- Меняем всех пустых кураторов на Супервайзера.
    -- Собираем всех кураторов из базы и делаем из них список.
    -- Итерация спискау кураторов.
    -- Вывод csv по каждому менеджеру. """

    outdir = 'outfile/' + form.out_name
    if not os.path.exists(outdir): os.makedirs(outdir)
    
    query = form.conn.cursor()
    query.execute("UPDATE vacant SET curator = 'Супервайзер' WHERE curator = ''")

    query.execute("SELECT curator FROM vacant GROUP BY curator;")
    curators = list(query)

    now = date.today().isoformat()                                         

    for curator, in curators:
        namef = "{}/{} {}.csv".format(outdir, now, curator)
        f = open(namef, 'w')
        query.execute("SELECT org, face, phone, phone_find, org_CRM,"
            "url, curator "
            "FROM vacant WHERE curator = '{}'".format(curator))
        for org, face, phone, phone_find, org_CRM, url, curator in query:
            s = ('{}${}${}$>{}<${}$=HYPERLINK("{}")${}\n'.
            format(org, face, phone, phone_find, org_CRM, url, curator))
            f.write(s)
        f.close()

    query.close()
